using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using UMC.Bridge;
using UMC.Net;

namespace UMC.ITME
{
    class EqualityComparer : IEqualityComparer<HttpSocks5>
    {
        public bool Equals(HttpSocks5 x, HttpSocks5 y)
        {
            return String.Equals(x.RepostValue, y.RepostValue);
        }

        public int GetHashCode([DisallowNull] HttpSocks5 obj)
        {
            return obj.GetHashCode();
        }
    }
    class HttpSocks5
    {
        public static IEqualityComparer<HttpSocks5> EqualityComparer => new EqualityComparer();

        static async Task Alert(Socket socket, String msg, String type = "msg")// IPAddress iPAddress, ushort port, CancellationToken cancellationToken = default)
        {
            var sb = new System.Text.StringBuilder();
            sb.Append("<script type=\"text/plain\" id=\"Config\">");
            sb.Append("{\"msg\":\"");
            sb.Append(msg);
            sb.Append("\",\"type\":\"");
            sb.Append(type);
            sb.Append("\"}");
            sb.Append("</script>");

            await HttpBridge.NotFind(403, "socks5", new NetworkStream(socket, true), sb.ToString().UTF8());
        }
        static async Task Auth(Socket socket, Socks5 socks5, StringValue key, String msg, StringValue sessionKey)// IPAddress iPAddress, ushort port, CancellationToken cancellationToken = default)
        {
            var IdKey = Utility.ToBase64String(key.Span);
            var url = $"https://{socks5.Host}/UMC/ITME/Device/{IdKey}/{Utility.ToBase64String(sessionKey.Span)}";
            var sb = new System.Text.StringBuilder();
            sb.Append("<script type=\"text/plain\" id=\"Config\">");
            sb.Append("{\"msg\":\"");
            sb.Append(msg);
            sb.Append("\",\"id\":\"");
            sb.Append(IdKey);
            sb.Append("\",\"url\":\"");
            sb.Append(url);
            sb.Append("\",\"posurl\":\"");
            sb.Append($"https://{socks5.Host}/UMC/");
            sb.Append("\",\"wss\":\"");
            sb.Append($"https://{socks5.Host}/UMC.WS/{IdKey}");
            sb.Append("\",\"type\":\"auth\"}");
            sb.Append("</script>");
            await HttpBridge.NotFind(401, "socks5", new NetworkStream(socket, true), sb.ToString().UTF8());

        }
        Socks5 socks5;
        IPAddress address;
        ushort port;
        public ushort Port => port;
        public String RepostValue => _repost;

        String _repost;
        static int CheckValue(int port)
        {
            int i = 1;
            while ((port = port / 10) > 0)
            {
                i++;
            }
            return i;
        }
        static int GetPort(int port)
        {
            IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();

            // 获取TCP连接信息
            TcpConnectionInformation[] tcpTable = ipGlobalProperties.GetActiveTcpConnections();

            var tcpTable2 = ipGlobalProperties.GetActiveTcpListeners();

            // Console.WriteLine("Active TCP Connections:");
            var hash = new HashSet<int>();// { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, 6668, 6669, 6697 };
            foreach (TcpConnectionInformation tcpConnection in tcpTable)
            {
                hash.Add(tcpConnection.LocalEndPoint.Port);
                // Console.WriteLine($"Local endpoint: {tcpConnection.LocalEndPoint}, Remote endpoint: {tcpConnection.RemoteEndPoint}, State: {tcpConnection.State}, Local Port: {tcpConnection.LocalEndPoint.Port}, PID: {tcpConnection.OwningProcessId}");
            }
            foreach (var tcpConnection in tcpTable2)
            {
                hash.Add(tcpConnection.Port);
                // Console.WriteLine($"Local endpoint: {tcpConnection.LocalEndPoint}, Remote endpoint: {tcpConnection.RemoteEndPoint}, State: {tcpConnection.State}, Local Port: {tcpConnection.LocalEndPoint.Port}, PID: {tcpConnection.OwningProcessId}");
            }
            if (hash.Contains(port) == false)
            {
                return port;
            }
            var p = port * 10;
            while (ushort.MaxValue > p)
            {
                for (int i = 0; i < 10; i++)
                {
                    if (hash.Contains(p + i) == false)
                    {
                        return p + i;
                    }

                }
                p = p * 10;
            }
            var val = CheckValue(port);
            p = (int)Math.Pow(10, val + 1);
            // p = (port / 10);
            while (ushort.MaxValue > p)
            {
                for (int i = 1; i < 10; i++)
                {
                    var vport = p * i + port;
                    if (ushort.MaxValue > vport)
                    {
                        if (hash.Contains(p * i + port) == false)
                        {
                            return p + i;
                        }
                    }
                    else
                    {
                        break;
                    }

                }
                val++;
                p = (int)Math.Pow(10, val + 1);
            }
            p = port;
            while (hash.Contains(p) == false)
            {
                p = Random.Shared.Next(10000, 20000);
            }
            return p;

        }
        public int BindPort
        {
            get
            {
                if (_BindPort == 0)
                {
                    this._BindPort = GetPort(this.port);
                }
                return _BindPort;
            }
        }
        int _BindPort;
        public override int GetHashCode()
        {
            return _repost.GetHashCode();
        }
        public Socket Listen
        {
            get; set;
        }
        public Socks5 Socks => this.socks5;
        public HttpSocks5(String repost, String username, String sessionKey)
        {
            var t = new Uri($"https://{repost}");
            _repost = repost;
            var userInfo = t.UserInfo;

            var lId = Utility.Parse(userInfo, 0L);

            this._SessionKey = new StringValue(Utility.FromBase64String(sessionKey));
            this.socks5 = new Socks5(t.Host, t.Port, new StringValue(username), StringValue.Empty);
            var bs = BitConverter.GetBytes(lId);
            this.address = new IPAddress(bs.AsSpan(2, 4));
            this.port = BitConverter.ToUInt16(bs.AsSpan(6));

        }
        public void ReInit(String username, String sessionKey)
        {
            this._SessionKey = new StringValue(Utility.FromBase64String(sessionKey));
            this.socks5 = new Socks5(this.socks5.Host, this.socks5.Port, new StringValue(username), StringValue.Empty);
        }
        static bool Accept(Socket socket, SocketAsyncEventArgs eventArgs)
        {
            try
            {
                return socket.AcceptAsync(eventArgs);
            }
            catch
            {
                return true;
            }
        }
        public void Http(object sender, SocketAsyncEventArgs eventArgs)
        {
            do
            {
                var client = eventArgs.AcceptSocket;
                eventArgs.AcceptSocket = null;
                switch (eventArgs.SocketError)
                {
                    case SocketError.OperationAborted:
                        return;
                    case SocketError.Success:
                        var ip = (client.RemoteEndPoint as IPEndPoint).Address;
                        var ipvalue = new byte[16];
                        if (ip.TryWriteBytes(ipvalue, out var l))
                        {
                            var reId = new StringValue(ipvalue, 0, l);
                            var firewall = DataFactory.Instance().Firewall(reId);
                            switch (firewall?.AuthType)
                            {
                                case Entities.AuthType.Deny:
                                    client.Close();
                                    client.Dispose();
                                    return;
                            }
                            HttpsProxyAsync(client, this);
                        }
                        break;
                }
            } while (!Accept((Socket)sender, eventArgs));


        }

        static async Task Recharge(Socket socket, Socks5 socks5, String msg)// IPAddress iPAddress, ushort port, CancellationToken cancellationToken = default)
        {
            var url = $"https://{socks5.Host}/UMC/Http/Bridge/Recharge";
            var sb = new System.Text.StringBuilder();
            sb.Append("<script type=\"text/plain\" id=\"Config\">");
            sb.Append("{\"msg\":\"");
            sb.Append(msg);
            sb.Append("\",\"url\":\"");
            sb.Append(url);
            sb.Append("\",\"posurl\":\"");
            sb.Append($"https://{socks5.Host}/UMC/");
            sb.Append("\",\"type\":\"Recharge\"}");
            sb.Append("</script>");
            await HttpBridge.NotFind(402, "socks5", new NetworkStream(socket, true), sb.ToString().UTF8());

        }
        public StringValue SessionKey
        {
            get { return _SessionKey; }
            set
            {
                _SessionKey = value;
            }
        }
        StringValue _SessionKey = StringValue.Empty;
        public async Task<(bool?, String)> Check(CancellationToken cancellationToken = default)
        {
            byte[] buffers = System.Buffers.ArrayPool<byte>.Shared.Rent(512);
            try
            {
                using (var client = await NetProxy.Connect(this.socks5.Host, this.socks5.Port, 0))
                {

                    var socks5 = this.socks5;

                    buffers[0] = 0x05;
                    buffers[1] = 0x01;
                    if (socks5.Username.IsEmpty())
                    {
                        buffers[2] = 0x00;
                    }
                    else
                    {
                        buffers[2] = 0x02;
                    }

                    await client.SendAsync(new ArraySegment<byte>(buffers, 0, 3), cancellationToken);
                    await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 2), cancellationToken);
                    switch (buffers[1])
                    {
                        case 0x00:
                            break;
                        case 0x02:
                            if (this.socks5.Username.Length == 0)
                            {
                                this.address.TryWriteBytes(buffers, out var _size);
                                BitConverter.TryWriteBytes(buffers.AsSpan(_size), this.port);
                                _size += 2;
                                (client.RemoteEndPoint as IPEndPoint).Address.TryWriteBytes(buffers.AsSpan(_size), out var bLeng);
                                _size += bLeng;
                                return (false, "请用ITME扫一扫上图二维码来设置转发账户");

                            }
                            buffers[0] = 0x01;
                            int size = this.socks5.Username.Length;
                            buffers[1] = (byte)size;
                            this.socks5.Username.Span.CopyTo(buffers.AsSpan(2));
                            size += 2;
                            int pw = this.socks5.Password.Length;
                            if (pw > 0)
                            {
                                buffers[size] = (byte)pw;
                                size++;
                                this.socks5.Password.Span.CopyTo(buffers.AsSpan(size));
                                size += pw;
                            }
                            else
                            {
                                buffers[size] = 20;
                                size++;
                                this.Password(buffers.AsSpan(size));
                                size += 20;

                            }
                            await client.SendAsync(new ArraySegment<byte>(buffers, 0, size), cancellationToken);
                            await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 2), cancellationToken);
                            if (buffers[1] != 0x00)
                            {
                                return (false, "SOCKS5 authentication failed");
                                ;
                            }
                            break;
                        default:
                            return (false, "SOCKS5 authentication failed");
                            ;
                    }


                    buffers[0] = 0x05; // SOCKS version
                    buffers[1] = 0x01; // CONNECT command
                    buffers[2] = 0x00; // Reserved
                    buffers[3] = 0x03; // Address type (domain name)
                    this.address.TryWriteBytes(buffers.AsSpan(4), out var adsize);
                    if (adsize == 4)
                    {
                        buffers[3] = 0x01; // Address type (domain name)
                    }
                    else
                    {
                        buffers[3] = 0x04;
                    }
                    adsize += 4;
                    // int port = httpSocks.port;
                    buffers[adsize] = (byte)(port >> 8);
                    buffers[1 + adsize] = (byte)port;
                    await client.SendAsync(new ArraySegment<byte>(buffers, 0, adsize + 2), cancellationToken);//, SocketFlags.None);


                    int msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 4), cancellationToken);//, SocketFlags.None);
                    if (msize == 4)
                    {
                        switch (buffers[1])
                        {
                            case 0x00:
                                break;
                            case 0x02:
                                msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 4, buffers[2] - 1), cancellationToken);//, SocketFlags.None);

                                break;
                            case 0x03:
                                {
                                    var bufers = new byte[30];
                                    this.address.TryWriteBytes(bufers, out var size);
                                    // size += 2;
                                    BitConverter.TryWriteBytes(bufers.AsSpan(size), port);
                                    size += 2;

                                    // socket.RemoteEndPoint
                                    msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 4, buffers[2] - 1), cancellationToken);//, SocketFlags.None);


                                    (client.RemoteEndPoint as IPEndPoint).Address.TryWriteBytes(bufers.AsSpan(size), out var bLeng);
                                    size += bLeng;
                                    return (false, "请用ITME扫一扫上图二维码来设置转发账户");
                                }
                                ;
                            case 0x04:

                                msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 4, buffers[2] - 1), cancellationToken);//, SocketFlags.None);

                                return (null, buffers.AsSpan(3, msize + 1).UTF8());
                                ;


                            default:
                                return (false, "SOCKS5 authentication failed");
                        }
                    }
                    else
                    {
                        return (false, "SOCKS5 authentication failed");

                    }
                    switch (buffers[3])
                    {
                        case 0x01:
                        case 0x04:
                        case 0x03:
                            break;
                        default:
                            return (false, "Unknown address type");
                    }

                    return (true, $"转发连接成功，现在可以使用此端口连接目标服务了");
                }

            }
            finally
            {

                System.Buffers.ArrayPool<byte>.Shared.Return(buffers);
            }
        }
        static async Task Http(Socket socket, byte[] buffers, HttpSocks5 httpSocks, CancellationToken cancellationToken = default)
        {
            using (var client = await NetProxy.Connect(httpSocks.socks5.Host, httpSocks.socks5.Port, 0))
            {

                var socks5 = httpSocks.socks5;

                buffers[0] = 0x05;
                buffers[1] = 0x01;
                if (socks5.Username.IsEmpty())
                {
                    buffers[2] = 0x00;
                }
                else
                {
                    buffers[2] = 0x02;
                }

                await client.SendAsync(new ArraySegment<byte>(buffers, 0, 3), cancellationToken);
                await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 2), cancellationToken);
                switch (buffers[1])
                {
                    case 0x00:
                        break;
                    case 0x02:
                        if (httpSocks.socks5.Username.Length == 0)
                        {
                            httpSocks.address.TryWriteBytes(buffers, out var _size);
                            BitConverter.TryWriteBytes(buffers.AsSpan(_size), httpSocks.port);
                            _size += 2;
                            (client.RemoteEndPoint as IPEndPoint).Address.TryWriteBytes(buffers.AsSpan(_size), out var bLeng);
                            _size += bLeng;
                            await Auth(socket, httpSocks.socks5, new StringValue(buffers, 0, _size), "请用ITME扫一扫上图二维码来设置转发账户", httpSocks._SessionKey);

                            return;
                        }
                        buffers[0] = 0x01;
                        int size = httpSocks.socks5.Username.Length;
                        buffers[1] = (byte)size;
                        httpSocks.socks5.Username.Span.CopyTo(buffers.AsSpan(2));
                        size += 2;
                        int pw = httpSocks.socks5.Password.Length;
                        if (pw > 0)
                        {
                            buffers[size] = (byte)pw;
                            size++;
                            httpSocks.socks5.Password.Span.CopyTo(buffers.AsSpan(size));
                            size += pw;
                        }
                        else
                        {
                            buffers[size] = 20;
                            size++;
                            httpSocks.Password(buffers.AsSpan(size));
                            size += 20;

                        }
                        await client.SendAsync(new ArraySegment<byte>(buffers, 0, size), cancellationToken);
                        await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 2), cancellationToken);
                        if (buffers[1] != 0x00)
                        {
                            await Alert(socket, "SOCKS5 authentication failed");
                            return;
                        }
                        break;
                    default:
                        await Alert(socket, "SOCKS5 authentication failed");
                        return;
                }


                buffers[0] = 0x05; // SOCKS version
                buffers[1] = 0x01; // CONNECT command
                buffers[2] = 0x00; // Reserved
                buffers[3] = 0x03; // Address type (domain name)
                httpSocks.address.TryWriteBytes(buffers.AsSpan(4), out var adsize);
                if (adsize == 4)
                {
                    buffers[3] = 0x01; // Address type (domain name)
                }
                else
                {
                    buffers[3] = 0x04;
                }
                adsize += 4;
                int port = httpSocks.port;
                buffers[adsize] = (byte)(port >> 8);
                buffers[1 + adsize] = (byte)port;
                await client.SendAsync(new ArraySegment<byte>(buffers, 0, adsize + 2), cancellationToken);//, SocketFlags.None);


                int msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 4), cancellationToken);//, SocketFlags.None);
                if (msize == 4)
                {
                    switch (buffers[1])
                    {
                        case 0x00:
                            break;
                        case 0x02:
                            msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 4, buffers[2] - 1), cancellationToken);//, SocketFlags.None);

                            await Alert(socket, "SOCKS5 authentication failed");
                            break;
                        case 0x03:
                            {
                                var bufers = new byte[30];
                                httpSocks.address.TryWriteBytes(bufers, out var size);
                                // size += 2;
                                BitConverter.TryWriteBytes(bufers.AsSpan(size), port);
                                size += 2;

                                // socket.RemoteEndPoint
                                msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 4, buffers[2] - 1), cancellationToken);//, SocketFlags.None);


                                (client.RemoteEndPoint as IPEndPoint).Address.TryWriteBytes(bufers.AsSpan(size), out var bLeng);
                                size += bLeng;
                                await Auth(socket, socks5, new StringValue(bufers, 0, size), "请用ITME扫一扫上图二维码来设置转发账户", httpSocks._SessionKey);
                            }
                            return;
                        case 0x04:

                            msize = await client.ReceiveAsync(new ArraySegment<byte>(buffers, 4, buffers[2] - 1), cancellationToken);//, SocketFlags.None);

                            await Recharge(socket, socks5, buffers.AsSpan(3, msize + 1).UTF8());
                            return;


                        default:
                            await Alert(socket, "SOCKS5 authentication failed");
                            // await HttpBridge.NotFind(200, "socks5", socket, "SOCKS5 authentication failed".UTF8());
                            return;
                    }
                }
                else
                {
                    await Alert(socket, "SOCKS5 authentication failed");
                    // await HttpBridge.NotFind(200, "socks5", socket, "SOCKS5 authentication failed".UTF8());
                    return;

                }
                switch (buffers[3])
                {
                    case 0x01:
                    case 0x04:
                    case 0x03:
                        break;
                    default:
                        await Alert(socket, "Unknown address type");
                        // await HttpBridge.NotFind(200, "socks5", socket, "Unknown address type.".UTF8());
                        // throw new Exception("Unknown address type.");
                        return;
                        // break;
                }
                // IPEndPoint pEndPoint = (IPEndPoint)socket.LocalEndPoint;

                await Alert(socket, $"转发连接成功，现在可以使用此端口连接目标服务了", "success");
            }
        }
        void Password(Span<byte> bytes)
        {
            var time = Utility.TimeSpan();
            System.Buffers.Binary.BinaryPrimitives.WriteUInt32BigEndian(bytes, time);

            int size = 4;
            int sIndex = this.socks5.Username.Span.IndexOf((byte)'@');
            if (sIndex > -1)
            {
                size += sIndex;
                this.socks5.Username.Slice(0, sIndex).Span.CopyTo(bytes.Slice(4));
            }
            else
            {

                size += this.socks5.Username.Length;
                this.socks5.Username.Span.CopyTo(bytes.Slice(4));
            }
            this._SessionKey.Span.CopyTo(bytes.Slice(size));
            size += 16;

            // Console.WriteLine($":{time}.{this.socks5.Username}.{Utility.ToBase64String(this.SessionKey.Span)}");

            Utility.MD5(bytes.Slice(0, size), bytes.Slice(size));
            bytes.Slice(size, 16).CopyTo(bytes.Slice(4));

        }
        static async void HttpsProxyAsync(Socket client, HttpSocks5 httpSocks)
        {
            bool isProxy = false;
            try
            {
                using (var byteChunk = new ByteChunk())
                {
                    int length = await client.ReceiveAsync(new System.ArraySegment<byte>(byteChunk.Chunk, 0, byteChunk.Chunk.Length));

                    using (var proxySocket = await NetProxy.Connect(httpSocks.socks5.Host, httpSocks.socks5.Port, 0))
                    {
                        var buffers = byteChunk.Chunk;
                        var timeOut = new CancellationTokenSource(60000);
                        if (byteChunk.Chunk.AsSpan(0, length).StartsWith("GET /"u8))
                        {
                            if (byteChunk.Chunk.AsSpan(0, length).StartsWith("GET /?user="u8))
                            {
                                int t = Array.FindIndex(byteChunk.Chunk, 5, r => r == ' ');
                                httpSocks.socks5 = new Socks5(httpSocks.socks5.Host, httpSocks.socks5.Port, new StringValue(byteChunk.Chunk.AsSpan(11, t - 11).ToArray()), httpSocks.socks5.Password);
                                // socks5.Username = new StringValue(byteChunk.Chunk.AsSpan(11, t - 11).ToArray());


                            }

                            await Http(client, byteChunk.Chunk, httpSocks, timeOut.Token);
                        }
                        else if (length > 0)
                        {
                            var startButt = byteChunk.Chunk.AsSpan(0, length).ToArray();

                            var socks5 = httpSocks.socks5;
                            buffers[0] = 0x05;
                            buffers[1] = 0x01;
                            if (socks5.Username.IsEmpty())
                            {
                                buffers[2] = 0x00;
                            }
                            else
                            {
                                buffers[2] = 0x02;
                            }

                            await proxySocket.SendAsync(new ArraySegment<byte>(buffers, 0, 3), timeOut.Token);
                            await proxySocket.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 2), timeOut.Token);
                            switch (buffers[1])
                            {
                                case 0x00:
                                    break;
                                case 0x02:
                                    buffers[0] = 0x01;
                                    int size = socks5.Username.Length;
                                    buffers[1] = (byte)size;
                                    socks5.Username.Span.CopyTo(buffers.AsSpan(2));
                                    size += 2;

                                    int pw = httpSocks.socks5.Password.Length;
                                    if (pw > 0)
                                    {
                                        buffers[size] = (byte)pw;
                                        size++;
                                        httpSocks.socks5.Password.Span.CopyTo(buffers.AsSpan(size));
                                        size += pw;
                                    }
                                    else
                                    {
                                        buffers[size] = 20;
                                        size++;
                                        httpSocks.Password(buffers.AsSpan(size));
                                        size += 20;

                                    }
                                    await proxySocket.SendAsync(new ArraySegment<byte>(buffers, 0, size), timeOut.Token);
                                    await proxySocket.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 2), timeOut.Token);
                                    if (buffers[1] != 0x00)
                                    {
                                        return;
                                    }
                                    break;
                                default:
                                    return;
                            }


                            buffers[0] = 0x05; // SOCKS version
                            buffers[1] = 0x01; // CONNECT command
                            buffers[2] = 0x00; // Reserved
                            buffers[3] = 0x03; // Address type (domain name)
                            httpSocks.address.TryWriteBytes(buffers.AsSpan(4), out var adsize);
                            if (adsize == 4)
                            {
                                buffers[3] = 0x01; // Address type (domain name)
                            }
                            else
                            {
                                buffers[3] = 0x04;
                            }
                            adsize += 4;
                            int port = httpSocks.port;
                            buffers[adsize] = (byte)(port >> 8);
                            buffers[1 + adsize] = (byte)port;
                            await proxySocket.SendAsync(new ArraySegment<byte>(buffers, 0, adsize + 2), timeOut.Token);//, SocketFlags.None);

                            // var responseHeader = new byte[4];
                            await proxySocket.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 4), timeOut.Token);//, SocketFlags.None);

                            if (buffers[1] != 0x00)
                            {
                                return;
                            }
                            int addressLength;
                            var addressType = buffers[3];
                            switch (addressType)
                            {
                                case 0x01:
                                    addressLength = 4;
                                    break;
                                case 0x04:
                                    addressLength = 16;
                                    break;
                                case 0x03:
                                    await proxySocket.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 1), timeOut.Token);//, SocketFlags.None);
                                    addressLength = buffers[0];
                                    break;
                                default:
                                    return;
                                    // break;
                            }
                            await proxySocket.ReceiveAsync(new ArraySegment<byte>(buffers, 0, addressLength + 2), timeOut.Token);
                            await proxySocket.SendAsync(new ArraySegment<byte>(startButt));

                            isProxy = true;
                            var clientRelay = new RelayAsyncArgs.Relay(proxySocket);
                            var proxyRelay = new RelayAsyncArgs.Relay(proxySocket);
                            new RelayAsyncArgs(clientRelay, proxyRelay);
                            new RelayAsyncArgs(proxyRelay, clientRelay);
                            // await Task.WhenAll(Socks5Server.RelayAsync(client, proxySocket), Socks5Server.RelayAsync(proxySocket, client));
                            // return client;
                        }
                    }
                }
            }
            catch
            {

            }
            finally
            {
                // client.
                if (isProxy == false)
                {
                    client.Close();
                    client.Dispose();
                }
            }
        }
    }
}